--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-----------------READ BEFORE EDITING--------------------------------------------
-----------------READ BEFORE EDITING--------------------------------------------
-----------------READ BEFORE EDITING--------------------------------------------
--DO ANYTHING YOU WANT, NO NEED TO ASK
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

function widget:GetInfo()
  return {
    name      = "Ribbon Effects",
    desc      = "A test for Ribbon effects",
    author    = "user, again i finished it.(was originally by quantum)",
    date      = "",
    license   = "GNU GPL, v2 or later",
    layer     = 1,
    enabled   = true  --  loaded by default?
  }
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

local gl            = gl --  use a local copy for faster access
local GL            = GL
local Spring        = Spring
local widgetHandler = widgetHandler
local math          = math
local e = Spring.Echo
local H = Spring.GetGroundHeight

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

local overlay = 0
local additive = 1
local subtractive = 2
local lines = 0
local points = 1
local quads = 2
local mx = Game.mapSizeX
local mz = Game.mapSizeZ
local gravity = Game.gravity 

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

local units = {}
local timer = 0

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

function widget:Initialize()
  local allUnits = Spring.GetAllUnits()
  for _, unitID in ipairs(allUnits) do
    units[unitID] = false
  end
end


function widget:UnitCreated(unitID)
   units[unitID] = false
end

local GenerateTrails = function(minimap,shadow,water)
  local toRemove = {}
  local timer = widgetHandler:GetHourTimer()
  for unitID, head in pairs(units) do
    local x, y, z = Spring.GetUnitPosition(unitID)

    if (not x) then -- if killed or out of LOS, discard unit
       toRemove[unitID] = nil
       break
    end

    local id = Spring.GetUnitDefID(unitID)
    local teamID = Spring.GetUnitTeam(unitID)
    local sx,sy,sz = Spring.GetUnitVelocity(unitID)

    if (minimap and not tonumber(UnitDefs[id].customParams.traildrawinminimap)==1) then break end  
    if (shadow and not tonumber(UnitDefs[id].customParams.traildrawshadow)==1) then break end  
    if (water and not tonumber(UnitDefs[id].customParams.traildrawwater)==1) then break end 
     
    if (UnitDefs[id].customParams.usetrail) then

    --read data from the custom unit def parameters      

    local piecelist    = Spring.GetUnitPieceList(unitID)
    local tcr 	     = UnitDefs[id].customParams.trailr
    local tcg 	     = UnitDefs[id].customParams.trailg
    local tcb	     = UnitDefs[id].customParams.trailb
    local sta 	     = UnitDefs[id].customParams.traila
    local linewidth    = tonumber(UnitDefs[id].customParams.trailwidth)  
    local lifeTime     = UnitDefs[id].customParams.trailframes
    local blendstring  = UnitDefs[id].customParams.trailblendmode 
    local fadealpha    = UnitDefs[id].customParams.trailfadealpha   
    local renderstring = UnitDefs[id].customParams.trailrendermode    
    local msx          = UnitDefs[id].customParams.trailminspeedx
    local msy          = UnitDefs[id].customParams.trailminspeedy
    local msz          = UnitDefs[id].customParams.trailminspeedz
    local wait         = UnitDefs[id].customParams.trailwait
    local vectors      = UnitDefs[id].customParams.traildrawvector
    local bb           = UnitDefs[id].customParams.trailbillboarded
    local sizemod      = UnitDefs[id].customParams.trailsizemod
    local tex          = UnitDefs[id].customParams.trailtexture
    local gravmod      = UnitDefs[id].customParams.trailgravity
    local hitground    = UnitDefs[id].customParams.trailhitground
   
    local blendmode    = 0  
    local rendermode   = 0

    if (renderstring == "points") then rendermode = GL.POINTS end 
    if (renderstring == "lines") then rendermode = GL.LINES end 
    if (renderstring == "quads") then rendermode = GL.QUAD_STRIP end 
    if (blendstring == "overlay") then blendmode = 0 end 
    if (blendstring == "additive" or blendstring == "add") then blendmode = 1 end 
    if (blendstring == "subtractive" or blendstring == "sub") then blendmode = 2 end 
    if (blendstring == "multiply" or blendstring == "mul") then blendmode = 3 end 
    if (blendstring == "divide" or blendstring == "div") then blendmode = 4 end 

    if (not linewidth) then linewidth = 2.00 end 
    if (not lifeTime) then lifeTime = 1.00 end 
    if (not blendmode) then blendmode = 0 end            
    if (not msx) then msx = 0 end 
    if (not msy) then msy = 0 end 
    if (not msz) then msz = 0 end 
    if (not rendermode) then rendermode = 0 end
    if (not tonumber(wait)) then wait = 1 end  
    if (not sizemod) then sizemod = 1 end
    if (not gravmod) then gravmod = 1 end
    

    units[unitID] = { units[unitID], x, y, z, timer + lifeTime - 0.001 , sx, sy, sz , Spring.GetGameFrame(), vectors, tonumber(sizemod), ((gravity*0.02)*gravmod), 0}
 
    local elements = {}
    local h = units[unitID]

    if (bb == "true") then gl.Billboard() end
    if (tex) then gl.Texture(tex) end

    gl.DepthTest(true) 

    if (tonumber(blendmode) == 0) then  
       gl.Blending(true)
    elseif (tonumber(blendmode) == 1) then  
       gl.Blending(GL.SRC_COLOR,GL.ONE)
    elseif (tonumber(blendmode) == 2) then  
       gl.Blending(GL.ONE_MINUS_SRC_COLOR,GL.ONE)
    end   
   
    if (minimap == true) then gl.PointSize(linewidth/64); gl.LineWidth(linewidth/64); end 
    if (minimap == false) then gl.PointSize(linewidth); gl.LineWidth(linewidth); end 

    gl.BeginEnd(rendermode,function() 
       local tx1,tx2 = 1,0
       local ty1,ty2 = 1,1 
       local c = 0
       while h do
          local timeLeft = math.mod((h[5] - timer) + 3600.0, 3600.0)
          if (tonumber(timeLeft) > tonumber(lifeTime)) then
          if (h == units[unitID]) then
             units[unitID] = nil
          end
          h[1] = nil
          else 
             local sxu,syu,szu = h[6],h[7],h[8]
             local asx,asy,asz = math.abs(sxu),math.abs(syu),math.abs(szu)
             local vec         = h[10]             
             local a
        	 local smod = h[11]
        	 local min,max = math.min(asx,asy,asz),math.max(asx,asy,asz)
        	 local h2 = h[1] or h 
        	 local myoff = h[12]
        	 local position = h[13] - myoff
        	 h[13] = position
        	 if (h[9] % tonumber(wait) == 0) then
           	    if (tonumber(fadealpha)==1) then
                   a = (timeLeft / lifeTime) - (1.00 - sta) 
                end 
             if (tonumber(fadealpha)==0) then   
                a = tonumber(sta)
             end  
             if ((asx < tonumber(msx)) and (asy < tonumber(msy)) and (asz < tonumber(msz))) then                                                               
                 a = 0.00 
             end  
             if (smod == 0) then else
                 smod = linewidth - ((a*linewidth)-smod)
             end        
             if (smod > linewidth) then a = 0.00 end
             if ((smod < 0) and (h[11] > 0)) then a = 0.00 end 
             if (vec == "xyz") then    --will do nothing
             elseif (vec == "yz") then
                if (max == asx) then a = 0; end 
             elseif (vec == "xz") then
                if (max == asy) then a = 0; end 
             elseif (vec == "xy") then
                if (max == asz) then a = 0; end 
             elseif (vec == "z") then
                if (max == asx) then a = 0; end 
                if (max == asy) then a = 0; end 
             elseif (vec == "x") then
                if (max == asy) then a = 0; end 
                if (max == asz) then a = 0; end 
             elseif (vec == "y") then
                if (max == asx) then a = 0; end 
                if (max == asz) then a = 0; end
             end   
             local yoff = (asy/(linewidth*0.2))-smod*0.5
             gl.Color(tcr,tcg,tcb,a) 
             if (rendermode == GL.QUAD_STRIP) then 
                if (smod < linewidth) then 
                   if (c == 0) then
                       gl.TexCoord(0,1)
                       gl.Vertex(h[2],(h[3]+position)+(linewidth-smod),h[4]-yoff)
                       gl.TexCoord(1,1)
                       gl.Vertex(h[2],(h[3]+position)-(linewidth-smod),h[4]+yoff)
                   elseif (c == 1) then
                       gl.TexCoord(0,0)
               	     gl.Vertex(h[2],(h[3]+(position-myoff))+(linewidth-smod),h[4]-yoff)
                 	     gl.TexCoord(1,0)
                 	     gl.Vertex(h[2],(h[3]+(position-myoff))-(linewidth-smod),h[4]+yoff)
                   end
                end 
             else gl.Vertex(h[2],(h[3]+position),h[4]) 
             end
          end
          if (c == 0) then c = 1 else c = 0 end
       end
       h = h[1] 
       end 
    end)

    end 

    gl.ResetState()

  end
 
  for unitID in pairs(toRemove) do
    units[unitID] = nil
  end
 
end

function widget:DrawWorld()
   gl.Frustum(5,5,5,5,5,5) -- wft?
   GenerateTrails(false,false,false)
end

function widget:DrawWorldReflection()
   gl.Frustum(5,5,5,5,5,5)
   GenerateTrails(false,false,true)
end

function widget:DrawWorldRefraction()
   gl.Frustum(5,5,5,5,5,5)
   GenerateTrails(false,false,true)
end

function widget:DrawWorldShadow()
   gl.Frustum(5,5,5,5,5,5)
   GenerateTrails(false,true,false)
end

function widget:DrawInMiniMap()
  gl.PushMatrix()
  gl.LoadIdentity()
  gl.Translate(0, 1, 0)
  gl.Scale(1 / mx, 1 / mz, 1)
  gl.Rotate(90, 1, 0, 0)
  gl.Frustum(5,5,5,5,5,5)
  GenerateTrails(true,false,false)
  gl.PopMatrix()
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------